package com.drg.center.service.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.alibaba.fastjson.JSONObject;
import com.drg.center.dao.*;
import com.drg.center.entity.*;
import com.drg.center.entity.enums.CaseSearchTypeEnum;
import com.drg.center.entity.enums.SexTypeEnum;
import com.drg.center.entity.param.*;
import com.drg.center.entity.result.*;
import com.drg.center.service.BaseDealService;
import com.drg.center.service.MedicalSettleService;
import com.guaike.core.entity.result.PageEntity;
import com.guaike.core.entity.result.Result;
import com.guaike.core.utils.ListUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@AllArgsConstructor
public class MedicalSettleServiceImpl implements MedicalSettleService {

    private final DrgPatientCaseOutDAO drgPatientCaseOutDAO;
    private final DrgPatientCaseDiagnosisDAO drgPatientCaseDiagnosisDAO;
    private final DrgPatientCaseOperationDAO drgPatientCaseOperationDAO;
    private final DrgPatientCasePersonInfoDAO drgPatientCasePersonInfoDAO;
    private final BaseDealService baseDealService;
    private final DictDrgDAO dictDrgDAO;

    @Override
    public Result<MedicalSettleVO> medicalSettle(MedicalSettleDTO param) {
        if (Objects.nonNull(param.getSettleEndDate())) {
            param.setSettleEndDate(param.getSettleEndDate().plusDays(1));
        }
        if (Objects.nonNull(param.getInEndDate())) {
            param.setInEndDate(param.getInEndDate().plusDays(1));
        }
        MedicalSettleVO vo = new MedicalSettleVO();
        param.setType(null);
        long totalCase = drgPatientCaseOutDAO.countTotalCase(param);
        param.setType(CaseSearchTypeEnum.noUpload.getCode());
        long noUploadCount = drgPatientCaseOutDAO.countTotalCase(param);
        param.setType(CaseSearchTypeEnum.upload.getCode());
        long uploadCount = drgPatientCaseOutDAO.countTotalCase(param);
        param.setType(CaseSearchTypeEnum.auditException.getCode());
        long auditExceptionCount = drgPatientCaseOutDAO.countTotalCase(param);
        param.setType(CaseSearchTypeEnum.noSettle.getCode());
        long noSettleCount = drgPatientCaseOutDAO.countTotalCase(param);
        param.setType(CaseSearchTypeEnum.none.getCode());
        long noneCount = drgPatientCaseOutDAO.countTotalCase(param);
        param.setType(CaseSearchTypeEnum.QY.getCode());
        long qyCount = drgPatientCaseOutDAO.countTotalCase(param);
        vo.setTotalCaseCount((int) totalCase);
        vo.setNoUploadCount((int) noUploadCount);
        vo.setUploadCount((int) uploadCount);
        vo.setAuditExceptionCount((int) auditExceptionCount);
        vo.setNoSettleCount((int) noSettleCount);
        vo.setNoneCount((int) noneCount);
        vo.setQyCount((int) qyCount);
        return Result.success().data(vo).build();
    }

    @Override
    public Result<PageEntity<MedicalSettlePageVO>> medicalSettlePage(MedicalSettlePageDTO param) {
        if (Objects.nonNull(param.getSettleEndDate())) {
            param.setSettleEndDate(param.getSettleEndDate().plusDays(1));
        }
        if (Objects.nonNull(param.getInEndDate())) {
            param.setInEndDate(param.getInEndDate().plusDays(1));
        }
        int start = (param.getPageIndex() - 1) * param.getPageSize();
        PageEntity<MedicalSettlePageVO> result = new PageEntity<>();
        long total = drgPatientCaseOutDAO.findPageCount(param);
        if (total == 0) {
            result.setTotal(total);
            result.setList(new ArrayList<>());
            return Result.success().data(result).build();
        }
        List<MedicalSettlePageVO> vos = drgPatientCaseOutDAO.findPageList(start, param);
        vos.forEach(vo -> {
            DrgPatientCaseDiagnosisPO diagnosisPO = drgPatientCaseDiagnosisDAO.findMainOne(vo.getCaseCode());
            DrgPatientCaseOperationPO operationPO = drgPatientCaseOperationDAO.findMainOne(vo.getCaseCode());
            vo.setDiagnosisName(diagnosisPO == null ? null : diagnosisPO.getDiagnosisName());
            vo.setOperationName(operationPO == null ? null : operationPO.getOperationName());
        });
        result.setTotal(total);
        result.setList(vos);
        return Result.success().data(result).build();
    }

    @Override
    public void exportExcel(HttpServletResponse response, MedicalSettlePageDTO param) throws Exception {
        int start = (param.getPageIndex() - 1) * param.getPageSize();
        List<MedicalSettlePageVO> vos = drgPatientCaseOutDAO.findPageList(start, param);
        List<MedicalSettleExcelVO> list = new ArrayList<>();
        vos.forEach(vo -> {
            MedicalSettleExcelVO excelVO = new MedicalSettleExcelVO();
            DrgPatientCaseDiagnosisPO diagnosisPO = drgPatientCaseDiagnosisDAO.findMainOne(vo.getCaseCode());
            DrgPatientCaseOperationPO operationPO = drgPatientCaseOperationDAO.findMainOne(vo.getCaseCode());
            vo.setDiagnosisName(diagnosisPO.getDiagnosisName());
            vo.setOperationName(operationPO.getOperationName());
            BeanUtils.copyProperties(vo, excelVO);
            excelVO.setIsNone(vo.getIsNone() ? "是" : "否");
            excelVO.setStatus(vo.getStatus() ? "是" : "否");
            excelVO.setUploadTime(LocalDateTimeUtil.format(vo.getUploadTime(), "yyyy-MM-dd HH:mm:ss"));
            if (Objects.nonNull(vo.getPatientAge())) {
                excelVO.setPatientAge(vo.getPatientAge() + "岁");
            }
            if (Objects.nonNull(vo.getAgeDay())) {
                excelVO.setPatientAge(vo.getAgeDay() + "天");
            }
            excelVO.setInHospitalDate(LocalDateTimeUtil.format(vo.getInHospitalDate(), "yyyy-MM-dd HH:mm:ss"));
            excelVO.setOutHospitalDate(LocalDateTimeUtil.format(vo.getOutHospitalDate(), "yyyy-MM-dd HH:mm:ss"));
            excelVO.setSettleDate(LocalDateTimeUtil.format(vo.getSettleDate(), "yyyy-MM-dd HH:mm:ss"));
            list.add(excelVO);
        });
        OutputStream outputStream = getOutputStream(response, "医保结算审核病例表格.xlsx");
        EasyExcel.write(outputStream, MedicalSettleExcelVO.class)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet("病例表格")
                .doWrite(list);
    }

    @Override
    public Result<Boolean> noneUpload(NoneUploadDTO param) {
        drgPatientCaseOutDAO.noneUpload(param.getCaseCodeList());
        return Result.success().data(Boolean.TRUE).build();
    }

    @Override
    public Result<MedicalSettleDetailVO> diagnosisAndOperation(String caseCode) {
        List<DrgPatientCaseDiagnosisPO> diagnosisPOS = drgPatientCaseDiagnosisDAO.findList(DrgPatientCaseDiagnosisPO::getCaseCode, caseCode);
        List<CaseDiagnosisVO> diagnosisVOS = ListUtil.copyList(diagnosisPOS, CaseDiagnosisVO.class);
        List<DrgPatientCaseOperationPO> operationPOS = drgPatientCaseOperationDAO.findList(DrgPatientCaseOperationPO::getCaseCode, caseCode);
        List<CaseOperationVO> operationVOS = ListUtil.copyList(operationPOS, CaseOperationVO.class);
        MedicalSettleDetailVO vo = new MedicalSettleDetailVO();
        vo.setCaseDiagnosisVOS(diagnosisVOS);
        vo.setCaseOperationVOS(operationVOS);
        return Result.success().data(vo).build();
    }

    @Override
    public Result<Boolean> diagnosisAndOperationSave(MedicalSettleDetailDTO param) {
        DrgPatientCaseOutPO caseOutPO = drgPatientCaseOutDAO.findOne(DrgPatientCaseOutPO::getCaseCode, param.getCaseCode());
        if (CollectionUtils.isNotEmpty(param.getCaseDiagnosisDelIds())) {
            drgPatientCaseDiagnosisDAO.delIn(DrgPatientCaseDiagnosisPO::getId, param.getCaseDiagnosisDelIds());
        }
        if (CollectionUtils.isNotEmpty(param.getCaseOperationDelIds())) {
            drgPatientCaseOperationDAO.delIn(DrgPatientCaseOperationPO::getId, param.getCaseOperationDelIds());
        }
        List<CaseDiagnosisDTO> diagnosisDTOS = param.getCaseDiagnosisDTOS();
        List<CaseDiagnosisDTO> saveDtos = diagnosisDTOS.stream().filter(dto -> Objects.isNull(dto.getId())).collect(Collectors.toList());
        List<CaseDiagnosisDTO> upDtos = diagnosisDTOS.stream().filter(dto -> Objects.nonNull(dto.getId())).collect(Collectors.toList());
        List<DrgPatientCaseDiagnosisPO> diagnosisPOS = ListUtil.copyList(upDtos, DrgPatientCaseDiagnosisPO.class);
        drgPatientCaseDiagnosisDAO.updateBatchById(diagnosisPOS);
        List<DrgPatientCaseDiagnosisPO> saveDiagnosisPOS = ListUtil.copyList(saveDtos, DrgPatientCaseDiagnosisPO.class);
        if (CollectionUtils.isNotEmpty(saveDiagnosisPOS)) {
            saveDiagnosisPOS = saveDiagnosisPOS.stream().peek(po -> {
                po.setCaseCode(param.getCaseCode());
                po.setDepartCode(caseOutPO.getDepartCode());
                po.setDepartName(caseOutPO.getDepartName());
                po.setDoctorCode(caseOutPO.getDoctorCode());
                po.setDoctorName(caseOutPO.getDoctorName());
            }).collect(Collectors.toList());
            drgPatientCaseDiagnosisDAO.saveBatch(saveDiagnosisPOS);
        }
        List<CaseOperationDTO> operationDTOS = param.getCaseOperationDTOS();
        List<CaseOperationDTO> saves = operationDTOS.stream().filter(dto -> Objects.isNull(dto.getId())).collect(Collectors.toList());
        List<CaseOperationDTO> updates = operationDTOS.stream().filter(dto -> Objects.nonNull(dto.getId())).collect(Collectors.toList());
        List<DrgPatientCaseOperationPO> operationPOS = ListUtil.copyList(updates, DrgPatientCaseOperationPO.class);
        drgPatientCaseOperationDAO.updateBatchById(operationPOS);
        List<DrgPatientCaseOperationPO> pos = ListUtil.copyList(saves, DrgPatientCaseOperationPO.class);
        if (CollectionUtils.isNotEmpty(pos)) {
            pos = pos.stream().peek(po -> po.setCaseCode(param.getCaseCode())).collect(Collectors.toList());
            drgPatientCaseOperationDAO.saveBatch(pos);
        }
        return Result.success().data(Boolean.TRUE).build();
    }

    @Override
    public Result<GroupPredictionVO> groupPrediction(MedicalSettleGroupPredictionDTO param) {
        DrgPatientCaseOutPO caseOutPO = drgPatientCaseOutDAO.findOne(DrgPatientCaseOutPO::getCaseCode, param.getCaseCode());
        DrgPatientCasePersonInfoPO personInfoPO = drgPatientCasePersonInfoDAO.findOne(DrgPatientCasePersonInfoPO::getCaseCode, param.getCaseCode());
        Integer sex;
        if (personInfoPO.getPatientSex().equals("男")) {
            sex = SexTypeEnum.man.getCode();
        } else {
            sex = SexTypeEnum.woman.getCode();
        }
        GroupPredictionVO vo = baseDealService.groupPrediction(sex, personInfoPO.getPatientAge(), personInfoPO.getAgeDay(), personInfoPO.getMedicareType(), caseOutPO.getTotalCost()
                , caseOutPO.getInpatientDays(), param.getDiagnosisCode(), param.getOperationCode(), param.getDiagnosisCodeList(), param.getOperationCodeList());
        return Result.success().data(vo).build();
    }


    @Override
    public void drgSettleStatExportExcel(HttpServletResponse response, SettleDateDTO param) throws Exception {
        List<DrgSettleExcelVO> list = new ArrayList<>();
        OutputStream outputStream = getOutputStream(response, "DRG结算统计表格.xlsx");
        EasyExcel.write(outputStream, DrgSettleExcelVO.class)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet("统计列表")
                .doWrite(list);
    }

    @Override
    public Result<PageEntity<CaseMonitorOutCheckVO>> checkDrgPage(CaseCheckPageDTO param) {
        PageEntity<CaseMonitorOutCheckVO> result = new PageEntity<>();
        long count = drgPatientCaseOutDAO.checkDrgPageCount(param);
        if (count == 0) {
            return Result.success().data(result).build();
        }
        int start = (param.getPageIndex() - 1) * param.getPageSize();
        List<CaseMonitorOutCheckVO> vos = drgPatientCaseOutDAO.checkDrgPage(start, param);
        vos.forEach(vo -> {
            vo.setStatus(vo.getDrgCode().equals(vo.getChsDrgCode()) ? 1 : 0);
        });
        result.setTotal(count);
        result.setList(vos);
        return Result.success().data(result).build();
    }

    @Override
    public Result<CaseDrgCheckVO> checkDrgDetail(String caseCode) {
        DrgPatientCaseOutPO caseOutPO = drgPatientCaseOutDAO.findOne(DrgPatientCaseOutPO::getCaseCode, caseCode);
        CaseDrgCheckVO vo = new CaseDrgCheckVO();
        CaseDrgBaseVO sysDrg = new CaseDrgBaseVO();
        CaseDrgBaseVO ybDrg = new CaseDrgBaseVO();
        sysDrg.setDrgCode(caseOutPO.getChsDrgCode());
        sysDrg.setDrgName(caseOutPO.getChsDrgName());
        sysDrg.setCaseType(caseOutPO.getCaseType());
        sysDrg.setReferencePoints(randomRate(70, 100).toPlainString());
        sysDrg.setTotalCost(caseOutPO.getTotalCost());
        sysDrg.setTotalPoints(randomRate(1000, 2000).toPlainString());
        sysDrg.setExpectedSurplus(caseOutPO.getTotalCost().subtract(Objects.nonNull(caseOutPO.getDrgCostStandard()) ? caseOutPO.getDrgCostStandard() : new BigDecimal(0)));
        List<DrgPatientCaseDiagnosisPO> diagnosisPOS = drgPatientCaseDiagnosisDAO.findList(DrgPatientCaseDiagnosisPO::getCaseCode, caseCode);
        diagnosisPOS = diagnosisPOS.stream().filter(DrgPatientCaseDiagnosisPO::getIsUpload).collect(Collectors.toList());
        List<DrgPatientCaseOperationPO> operationPOS = drgPatientCaseOperationDAO.findList(DrgPatientCaseOperationPO::getCaseCode, caseCode);
        operationPOS = operationPOS.stream().filter(DrgPatientCaseOperationPO::getIsUpload).collect(Collectors.toList());
        DictDrgPO drgPO = dictDrgDAO.findOne(DictDrgPO::getDrgCode, caseOutPO.getDrgCode());
        ybDrg.setDrgCode(caseOutPO.getDrgCode());
        if (Objects.nonNull(drgPO)) {
            ybDrg.setDrgName(drgPO.getDrgName());
            ybDrg.setCaseType(caseOutPO.getCaseType());
            ybDrg.setReferencePoints(randomRate(70, 100).toPlainString());
            ybDrg.setTotalCost(caseOutPO.getTotalCost());
            ybDrg.setTotalPoints(randomRate(1000, 2000).toPlainString());
            ybDrg.setExpectedSurplus(caseOutPO.getTotalCost().subtract(Objects.nonNull(drgPO.getDrgCostStandard()) ? drgPO.getDrgCostStandard() : new BigDecimal(0)));
        }
        List<CaseMonitorDiagnosisVO> diagnosisVOS = ListUtil.copyList(diagnosisPOS, CaseMonitorDiagnosisVO.class);
        List<CaseMonitorOperationVO> operationVOS = ListUtil.copyList(operationPOS, CaseMonitorOperationVO.class);
        sysDrg.setDiagnosisVOS(diagnosisVOS);
        sysDrg.setOperationVOS(operationVOS);
        ybDrg.setDiagnosisVOS(diagnosisVOS);
        ybDrg.setOperationVOS(operationVOS);
        vo.setSysDrg(sysDrg);
        vo.setYbDrg(ybDrg);
        return Result.success().data(vo).build();
    }


    /**
     * 导出时生成OutputStream
     */
    private static OutputStream getOutputStream(HttpServletResponse response, String fileName) throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
        return response.getOutputStream();
    }

    private static BigDecimal randomRate(double min, double max) {
        return BigDecimal.valueOf(RandomUtil.randomDouble(min, max, 2, RoundingMode.HALF_UP));
    }
}
